package com.feonix.common.util;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;

public class JsonUtil {
	private static final Log logger = LogFactory.getLog(JsonUtil.class);

	static {
		Configuration.setDefaults(new Configuration.Defaults() {
			private JsonProvider jsonProvider = null;

			private MappingProvider mappingProvider = null;

			public JsonProvider jsonProvider() {
				if (this.jsonProvider == null) {
					ObjectMapper objectMapper = new ObjectMapper();
					objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
					objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
					objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
					this.jsonProvider = new JacksonJsonProvider(objectMapper);
				}
				return this.jsonProvider;
			}

			public MappingProvider mappingProvider() {
				if (this.mappingProvider == null) {
					ObjectMapper objectMapper = new ObjectMapper();
					objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
					objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
					objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
					this.mappingProvider = new JacksonMappingProvider(objectMapper);
				}
				return this.mappingProvider;
			}

			public Set<Option> options() {
				return EnumSet.noneOf(Option.class);
			}
		});
	}

	private static ObjectMapper getObjectMapper() {
		ObjectMapper objectMapper = new ObjectMapper();
		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
		objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
		return objectMapper;
	}

	public static String objectToString(Object object) {
		if (object == null) {
			return null;
		}
		ObjectMapper objectMapper = getObjectMapper();
		String str = null;
		try {
			str = objectMapper.writeValueAsString(object);
		} catch (Exception exception) {
			logger.error("ObjectToString error", exception);
		}
		return str;
	}

	public static <T> T StringToObject(String json, Class<T> clazz) {
		if (StringUtil.isEmpty(json)) {
			return null;
		}
		ObjectMapper objectMapper = getObjectMapper();
		T value = null;
		try {
			value = objectMapper.readValue(json, clazz);
		} catch (Exception exception) {
			logger.error("StringToObject error", exception);
		}
		return value;
	}

	public static <T> List<T> StringToList(String json, Class<?>[] clazz) {
		if (StringUtil.isEmpty(json)) {
			return null;
		}
		ObjectMapper objectMapper = getObjectMapper();
		JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, clazz);
		List<T> list = null;
		try {
			list = objectMapper.readValue(json, javaType);
		} catch (Exception exception) {
			logger.error("StringToObject error", exception);
		}
		return list;
	}

	public static String getValue(String json, String key) {
		if ((StringUtil.isEmpty(json)) && (StringUtil.isNotEmpty(key))) {
			return null;
		}
		ObjectMapper objectMapper = getObjectMapper();
		String str = null;
		try {
			JsonNode jsonNode = objectMapper.readTree(json);
			jsonNode = jsonNode.findPath(key);
			if (jsonNode != null)
				str = jsonNode.asText();
		} catch (Exception exception) {
			logger.error("getValue error", exception);
		}
		return str;
	}

	public static <T> T getValueFromPath(String json, String path) {
		if ((StringUtil.isEmpty(json)) && (StringUtil.isNotEmpty(path))) {
			return null;
		}
		T obj = null;
		try {
			obj = JsonPath.read(json, path, new Predicate[0]);
		} catch (Exception exception) {
			logger.error("getValue error:" + exception.getMessage());
		}
		return obj;
	}

}
